/* 
 * File:   mpi_ub6.cpp
 * Author: sergey
 */

#include "DefineConfiguration.h"
#include <iostream>
#include <sstream>
#include <mpi/mpi.h>
#include <fstream>
#include <omp.h>
#include <vector>
#include "LibraryInterface.h"
#include <string>

#include "min7/genmin7v0.0.cpp"

int rank = 0, size = 1;

struct UInt128 {
    UInt64 l, r;
};

const UInt8 fComplication = 27;

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    MPI_Comm_size(MPI_COMM_WORLD,&size);

    std::ostringstream filename;

    MPI_Status state;

    if (rank == 0) {
        std::cout << "Starting task generator." << std::endl;
        
        std::vector<UInt128> funcs;
        funcs.resize(size - 1);
        for(size_t i = size - 1; i--; ) {funcs[i].l = 0;funcs[i].r = 0;}
        
  
        UInt64 busy = 1;
        UInt64 doneFunctions = 0;

        std::ifstream inputFile;

        filename << LIBRARYDIRECTORIES << "/7/ub7_27-28_deep.bin";
        std::ofstream outputFile(filename.str().c_str(), std::ios::binary);
        filename.str("");

        filename << LIBRARYDIRECTORIES << "/7/ub7_27-28.bin";
        inputFile.open(filename.str().c_str(), std::ios::binary);
        filename.str("");
        UInt64 inputFileSize;
        inputFile.seekg(0, std::ios::end);
        inputFileSize = inputFile.tellg() / 16;
        inputFile.seekg(0, std::ios::beg);

        for(;!inputFile.eof();) {
            UInt128 nf;
            inputFile.read((char*)&nf, sizeof(UInt128));            
                
            if (busy == size) {
                UInt8 fc;
                doneFunctions++;
                MPI_Recv(&fc, 1, MPI_UNSIGNED_CHAR, MPI_ANY_SOURCE, 2, MPI_COMM_WORLD, &state);
                if ((doneFunctions & 0xFFF) == 0)
                    std::cout << "Processed functions " << std::dec << doneFunctions << std::endl;
                if (fc >= fComplication) {
                    std::cout << "Function (" << std::hex <<
                                    funcs[state.MPI_SOURCE - 1].l << ',' <<
                                    funcs[state.MPI_SOURCE - 1].r <<
                                ") has complication " << std::dec << (int) fc << std::endl;
                    outputFile.write((char*)&funcs[state.MPI_SOURCE - 1], sizeof(UInt128));
                }
                funcs[state.MPI_SOURCE - 1] = nf;
                MPI_Send(&funcs[state.MPI_SOURCE - 1], 2, MPI_UNSIGNED_LONG_LONG, state.MPI_SOURCE, 1, MPI_COMM_WORLD);
            } else {
                for(size_t j = 0; j < funcs.size(); j++)
                    if (funcs[j].l == 0 && funcs[j].r == 0) {
                        funcs[j] = nf;
                        MPI_Send(&funcs[j], 2, MPI_UNSIGNED_LONG_LONG, j + 1, 1, MPI_COMM_WORLD);

                        busy++;
                        break;
                    }
            }
            
        }
        inputFile.close();

        for(; busy != 1; busy--) {
            UInt8 fc;
            doneFunctions++;
            MPI_Recv(&fc, 1, MPI_UNSIGNED_CHAR, MPI_ANY_SOURCE, 2, MPI_COMM_WORLD, &state);
            if ((doneFunctions && 0xFFFF) == 0)
                std::cout << "Processed functions " << std::dec << doneFunctions << std::endl;
            if (fc >= fComplication) {
                std::cout << "Function (" << std::hex <<
                                funcs[state.MPI_SOURCE - 1].l << ',' <<
                                funcs[state.MPI_SOURCE - 1].r <<
                            ") has complication " << std::dec << (int) fc << std::endl;
                outputFile.write((char*)&funcs[state.MPI_SOURCE - 1], sizeof(UInt128));
            }
            funcs[state.MPI_SOURCE - 1].l = 0;
            funcs[state.MPI_SOURCE - 1].r = busy;
            MPI_Send(&funcs[state.MPI_SOURCE - 1], 2, MPI_UNSIGNED_LONG_LONG, state.MPI_SOURCE, 3, MPI_COMM_WORLD);
        }

        outputFile.close();
        std::cout << "Task generator ending work." << std::endl;
    } else {
        std::cout << "Starting processor: " << rank << std::endl;
        
        UInt128 f;        
        
        tf7_exp p;

        filename << LIBRARYDIRECTORIES << "/nllibs/nllibrary_" << Lib5::n << ".bin";
        nllib.load(filename.str());
        filename.str("");
        
        while(true) {
            
            MPI_Recv(&f, 2, MPI_UNSIGNED_LONG_LONG, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &state);

            if (state.MPI_TAG == 1) {   
                UInt8 fc = genmin7(f.l >> 32, f.l & 0xFFFFFFFF, f.r >> 32, f.r & 0xFFFFFFFF, p);
                MPI_Send(&fc, 1, MPI_UNSIGNED_CHAR, 0, 2, MPI_COMM_WORLD);
            } else {
                std::cout << "Process " << std::dec << rank << " ending work." << std::endl;
                break;
            }            
        }              
    }

    MPI_Finalize();
    
    return (0);
}
